<!DOCTYPE html>
<html>
<!--
Copyright The Closure Library Authors. All Rights Reserved.

Use of this source code is governed by the Apache License, Version 2.0.
See the COPYING file for details.
-->
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
  <title>Closure Performance Tests - goog.pubsub.PubSub</title>
  <link rel="stylesheet" href="../testing/performancetable.css" />
  <script src="../base.js"></script>
  <script>
    goog.require('goog.events');
    goog.require('goog.events.EventTarget');
    goog.require('goog.pubsub.PubSub');
    goog.require('goog.testing.PerformanceTable');
    goog.require('goog.testing.PerformanceTimer');
    goog.require('goog.testing.jsunit');
  </script>
</head>
<body>
  <h1>goog.pubsub.PubSub Performance Tests</h1>
  <p>
    <b>User-agent:</b> <script>document.write(navigator.userAgent);</script>
  </p>
  <p>
    Compares the performance of the event system (<code>goog.events.*</code>)
    with the <code>goog.pubsub.PubSub</code> class.
  </p>
  <p>
    The baseline test creates 1000 event targets and 1000 objects that handle
    events dispatched by the event targets, and has each event target dispatch
    2 events 5 times each.
  </p>
  <p>
    The single-<code>PubSub</code> test creates 1000 publishers, 1000
    subscribers, and a single pubsub channel.  Each subscriber subscribes to
    topics on the same pubsub channel.  Each publisher publishes 5 messages to
    2 topics each via the pubsub channel.
  </p>
  <p>
    The multi-<code>PubSub</code> test creates 1000 publishers that are
    subclasses of <code>goog.pubsub.PubSub</code> and 1000 subscribers.  Each
    subscriber subscribes to its own publisher.  Each publisher publishes 5
    messages to 2 topics each via its own pubsub channel.
  </p>
  <div id="perfTable"></div>
  <hr>
  <script>
    var targets, publishers, pubsubs, handlers;

    // Number of objects to test per run.
    var SAMPLES_PER_RUN = 1000;

    // The performance table & performance timer.
    var table, timer;

    // Event/topic identifiers.
    var ACTION = 'action';
    var CHANGE = 'change';

    // Number of times handlers have been called.
    var actionCount = 0;
    var changeCount = 0;

    // Generic event handler class.
    function Handler() {
    }
    Handler.prototype.handleAction = function() {
      actionCount++;
    };
    Handler.prototype.handleChange = function() {
      changeCount++;
    };

    // Generic publisher class that uses a global pubsub channel.
    function Publisher(pubsub, id) {
      this.pubsub = pubsub;
      this.id = id;
    }
    Publisher.prototype.publish = function(topic) {
      this.pubsub.publish(this.id + '.' + topic);
    };

    // PubSub subclass; allows clients to subscribe and uses itself to publish.
    function PubSub() {
      goog.pubsub.PubSub.call(this);
    }
    goog.inherits(PubSub, goog.pubsub.PubSub);

    // EventTarget subclass; uses goog.events.* to dispatch events.
    function Target() {
      goog.events.EventTarget.call(this);
    }
    goog.inherits(Target, goog.events.EventTarget);
    Target.prototype.fireEvent = function(type) {
      this.dispatchEvent(type);
    };

    function initHandlers(count) {
      for (var i = 0; i < count; i++) {
        handlers[i] = new Handler();
      }
    }

    function initPublishers(pubsub, count) {
      for (var i = 0; i < count; i++) {
        publishers[i] = new Publisher(pubsub, i);
      }
    }

    function initPubSubs(count) {
      for (var i = 0; i < count; i++) {
        pubsubs[i] = new PubSub();
      }
    }

    function initTargets(count) {
      for (var i = 0; i < count; i++) {
        targets[i] = new Target();
      }
    }

    function createEventListeners(count) {
      initHandlers(count);
      initTargets(count);
      for (var i = 0; i < count; i++) {
        goog.events.listen(targets[i], ACTION, Handler.prototype.handleAction,
            false, handlers[i]);
        goog.events.listen(targets[i], CHANGE, Handler.prototype.handleChange,
            false, handlers[i]);
      }
    }

    function createGlobalSubscriptions(pubsub, count) {
      initHandlers(count);
      initPublishers(pubsub, count);
      for (var i = 0; i < count; i++) {
        pubsub.subscribe(i + '.' + ACTION, Handler.prototype.handleAction,
            handlers[i]);
        pubsub.subscribe(i + '.' + CHANGE, Handler.prototype.handleChange,
            handlers[i]);
      }
    }

    function createSubscriptions(count) {
      initHandlers(count);
      initPubSubs(count);
      for (var i = 0; i < count; i++) {
        pubsubs[i].subscribe(ACTION, Handler.prototype.handleAction,
            handlers[i]);
        pubsubs[i].subscribe(CHANGE, Handler.prototype.handleChange,
            handlers[i]);
      }
    }

    function dispatchEvents(count) {
      for (var i = 0; i < count; i++) {
        for (var j = 0; j < 5; j++) {
          targets[i].fireEvent(ACTION);
          targets[i].fireEvent(CHANGE);
        }
      }
    }

    function publishGlobalMessages(count) {
      for (var i = 0; i < count; i++) {
        for (var j = 0; j < 5; j++) {
          publishers[i].publish(ACTION);
          publishers[i].publish(CHANGE);
        }
      }
    }

    function publishMessages(count) {
      for (var i = 0; i < count; i++) {
        for (var j = 0; j < 5; j++) {
          pubsubs[i].publish(ACTION);
          pubsubs[i].publish(CHANGE);
        }
      }
    }

    function setUpPage() {
      timer = new goog.testing.PerformanceTimer();
      timer.setNumSamples(10);
      timer.setTimeoutInterval(9000);
      timer.setDiscardOutliers(true);
      table = new goog.testing.PerformanceTable(
          goog.dom.getElement('perfTable'), timer);
    }

    function setUp() {
      actionCount = 0;
      changeCount = 0;
      handlers = [];
      publishers = [];
      pubsubs = [];
      targets = [];
    }

    function testCreateEventListeners() {
      table.run(goog.partial(createEventListeners, SAMPLES_PER_RUN),
          '1A: Create event listeners');
      assertEquals(0, actionCount);
      assertEquals(0, changeCount);
    }

    function testCreateGlobalSubscriptions() {
      var pubsub = new goog.pubsub.PubSub();
      table.run(
          goog.partial(createGlobalSubscriptions, pubsub, SAMPLES_PER_RUN),
          '1B: Create global subscriptions');
      assertEquals(SAMPLES_PER_RUN * timer.getNumSamples() * 2,
          pubsub.getCount());
      assertEquals(0, actionCount);
      assertEquals(0, changeCount);
      pubsub.dispose();
    }

    function testCreateSubscripions() {
      table.run(goog.partial(createSubscriptions, SAMPLES_PER_RUN),
          '1C: Create subscriptions');
      assertEquals(0, actionCount);
      assertEquals(0, changeCount);
    }

    function testDispatchEvents() {
      createEventListeners(SAMPLES_PER_RUN);
      table.run(goog.partial(dispatchEvents, SAMPLES_PER_RUN),
          '2A: Dispatch events');
      assertEquals(SAMPLES_PER_RUN * timer.getNumSamples() * 5, actionCount);
      assertEquals(SAMPLES_PER_RUN * timer.getNumSamples() * 5, changeCount);
    }

    function testPublishGlobalMessages() {
      var pubsub = new goog.pubsub.PubSub();
      createGlobalSubscriptions(pubsub, SAMPLES_PER_RUN);
      table.run(
          goog.partial(publishGlobalMessages, SAMPLES_PER_RUN),
          '2B: Publish global messages');
      assertEquals(SAMPLES_PER_RUN * timer.getNumSamples() * 5, actionCount);
      assertEquals(SAMPLES_PER_RUN * timer.getNumSamples() * 5, changeCount);
      pubsub.dispose();
    }

    function testPublishMessages() {
      createSubscriptions(SAMPLES_PER_RUN);
      table.run(goog.partial(publishMessages, SAMPLES_PER_RUN),
          '2C: Publish messages');
      assertEquals(SAMPLES_PER_RUN * timer.getNumSamples() * 5, actionCount);
      assertEquals(SAMPLES_PER_RUN * timer.getNumSamples() * 5, changeCount);
    }

    function testEvents() {
      table.run(function() {
        createEventListeners(SAMPLES_PER_RUN);
        dispatchEvents(SAMPLES_PER_RUN);
      }, '3A: Events');
      assertEquals(SAMPLES_PER_RUN * timer.getNumSamples() * 5, actionCount);
      assertEquals(SAMPLES_PER_RUN * timer.getNumSamples() * 5, changeCount);
    }

    function testSinglePubSub() {
      table.run(function() {
        var pubsub = new goog.pubsub.PubSub();
        createGlobalSubscriptions(pubsub, SAMPLES_PER_RUN);
        publishGlobalMessages(SAMPLES_PER_RUN);
        pubsub.dispose();
      }, '3B: Single PubSub');
      assertEquals(SAMPLES_PER_RUN * timer.getNumSamples() * 5, actionCount);
      assertEquals(SAMPLES_PER_RUN * timer.getNumSamples() * 5, changeCount);
    }

    function testMultiPubSub() {
      table.run(function() {
        createSubscriptions(SAMPLES_PER_RUN);
        publishMessages(SAMPLES_PER_RUN);
      }, '3C: Multi PubSub');
      assertEquals(SAMPLES_PER_RUN * timer.getNumSamples() * 5, actionCount);
      assertEquals(SAMPLES_PER_RUN * timer.getNumSamples() * 5, changeCount);
    }
  </script>
</body>
</html>
